'use strict'

const titbit = require('titbit')

const app = new titbit({
  http2: true,
  useLimit: true,
  maxConn: 500,
  strong: true,
  timeout: 2500,
})

let apiLimit = function () {

  if (!(this instanceof apiLimit)) {
    return new apiLimit()
  }

  this.taskTime = 120000 + parseInt( Math.random() * 10000)

  this.map = new Map()

  //最大缓存数量，已经存在的需要有过期删除。
  //单进程，单个Map，控制在30ms以内完成清理工作，极限在100000（AMD Ryzen 4500U）。
  this.maxIdCount = 30000

  //极限容量，当达到最大容量时，会触发清理操作。
  this.denyMax = this.maxCount + 500

  this.maxLimit = 30

  this.allow = [
    //允许调用的api路径h
  ];

  this.allowLimit = 100

  //单位时间，毫秒单位。
  this.unitTime = 60000

  //一天内可以调用接口次数
  this.dayLimit = 10000

  //一天按照12小时计算。
  this.dayExpires = 43200000

  this.lifeTime = this.peerTime * 50

  this.set = (id) => {

    let tm = Date.now()
    
    let val = {
      daystart : tm,
      daycount: 1,
      time : tm,
      count : 1
    }

    this.map.set(id, val)

    if (this.map.size >= this.denyMax) {
      this.remove(200)
    } else if (this.map.size > this.maxIdCount) {
      this.clean()
    }

  }

  this.get = (id) => {
    return this.map.get(id)
  }

  this.delete = (id) => {
    this.map.delete(id)
  }

  this.clear = () => {
    thic.map.clear()
  }

  this.checkAndSet = (id, path = null) => {
    let a = this.get(id)
    if (!a) {
      this.set(id)
      return true
    }

    return this.check(id, a, path)
  }

  this.check = (id, m, path = null) => {
    let tm = Date.now()
    if (this.dayLimit > 0) {
      if (tm - m.daystart > this.dayExpires) {
        m.daystart = tm
        m.daycount = 1
      } else if (m.daycount <= this.dayLimit) {
        m.daycount += 1
      }
    }

    if (this.maxLimit > 0) {
      if (tm - m.time > this.unitTime) {
        m.time = tm
        m.count = 1
      } else if (m.count <= this.allowLimit) {
        m.count += 1
      }
    }

    if (path && this.allow.indexOf(path) >= 0 && m.count <= this.allowLimit) {
      return true
    }

    if (m.daycount > this.dayLimit || m.count > this.maxLimit) {
      return false
    }

    return true
  }

  //可能不会清理任何数据。
  this.clean = (total = 1000) => {
    let count = 1
    let tm = Date.now()

    for (let [k, v] of this.map) {
      count += 1
      if (count > total) {
        break
      }
      if (this.needClean(v, tm)) {
        this.delete(k)
      }
    }
  }

  this.remove = (total = 100) => {
    let count = 0
    for (let [k,v] of this.map) {
      count += 1
      if (count > total) {
        break
      }
      this.map.delete(k)
    }
  }

  this.cleanInterval = null

  //清除是定时任务，检测每个需要清理的项。
  this.cleanTask = async () => {
    if (this.cleanInterval) {
      clearInterval(this.cleanInterval)
    }

    this.cleanInterval = setInterval(() => {
      this.startClean()
    }, this.taskTime)

  }

  this.startClean = () => {
    let tm = Date.now()
    for (let [k, v] of this.map) {
      if (this.needClean(v, tm)) {
        this.delete(k)
      }
    }
  }

  this.needClean = (m, tm) => {
    if (m.time + this.lifeTime < tm) {
      return true
    }
    return false
  }

}

let limitm = new apiLimit()

//定时清理
limitm.cleanTask()

/* setInterval(() => {
  console.log(limitm.map)
}, 1280) */

/**
 * 获取并设置，如果没有则设置。
 */
app.get('/:id', async c => {
  let r = limitm.checkAndSet(c.param.id, c.query.path ? c.query.path : null)

  if (r) {
    c.send('ok')
  } else {
    c.send('deny', 403)
  }

})

app.get('/allow', async c => {
  c.send(limitm.allow)
})

app.delete('/allow', async c => {
  while( limitm.allow.pop() ) {}
})

app.post('/allow', async c => {
  let list = JSON.parse(c.body)
  
  if (list instanceof Array) {
    for (let i = 0; i < list.length; i++) {
      limitm.allow.push(list[i])
    }
  } else {
    c.send('bad data', 400)
  }

})

app.delete('/:id', async c => {
  limitm.delete(c.param.id)
})

app.get('/config', async c => {
  c.send({
    maxIdCount: limitm.maxIdCount,
    maxLimit: limitm.maxLimit,
    allowLimit: limitm.allowLimit,
    unitTime: limitm.unitTime,
    lifeTime: limitm.lifeTime,
    dayLimit: limitm.dayLimit,
    dayExpires: limitm.dayExpires,
  })
})

app.post('/config', async c => {

  let cfg = JSON.parse(c.body)

  if (cfg.toString() !== '[object Object]') {
    c.send('bad data', 400)
    return
  }

  for (let k in cfg) {
    switch (k) {
      case 'maxIdCount':
      case 'maxLimit':
      case 'allowLimit':
      case 'unitTime':
      case 'lifeTime':
      case 'dayExpires':
      case 'dayLimit':
        if (typeof cfg[k] === 'number' && cfg[k] >= 0) {
          limitm[k] = parseInt(cfg[k])
          if (k === 'maxIdCount') {
            limitm.denyMax = cfg[k] + 500
          }
        }
        break
    }
  }

})

app.delete('/clear', async c => {
  limitm.clear()
})

app.run(10101, 'localhost')

//服务套接字不超时。
app.server.timeout = 0
